// <copyright file="BinaryStructure.cs" company="Largo"> // Copyright (c) 2009 All Right Reserved // </copyright> // <author> vl </author> // <email></email> // <date>2009-01-01</date> // <summary>Contains ...</summary> namespace LargoCommon.Music { using LargoCommon.Interfaces; using System; using System.Collections; using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using System.Xml.Serialization; /// <summary> /// Binary Structure. /// </summary> [Serializable] [XmlRoot] public class BinaryStructure : GeneralOwner, IComparable, IGeneralStruct { #region Fields /// <summary> /// Bit Array. /// </summary> private BitArray bitArray; /// <summary> /// Structural Code. /// </summary> private string structuralCode; /// <summary> /// General system. /// </summary> private GeneralSystem gsystem; //// readonly /// <summary> Number of nonzero bits, number of rotations. </summary> private byte level; #endregion #region Constructors /// <summary> Initializes a new instance of the BinaryStructure class. Serializable. </summary> public BinaryStructure() { } /// <summary> Initializes a new instance of the BinaryStructure class. </summary> /// <param name="givenSystem">Abstract system.</param> /// <param name="givenStructuralCode">Structural code.</param> public BinaryStructure(GeneralSystem givenSystem, string givenStructuralCode) { Contract.Requires(givenSystem != null); this.gsystem = givenSystem; this.SetStructuralCode(givenStructuralCode); this.DetermineLevel(); //// 2014/12 Time optimization //// this.CheckInstance(); } /// <summary> Initializes a new instance of the BinaryStructure class. </summary> /// <param name="givenSystem">Abstract system.</param> /// <param name="givenBitArray">Bit array.</param> public BinaryStructure(GeneralSystem givenSystem, BitArray givenBitArray) { Contract.Requires(givenSystem != null); this.gsystem = givenSystem; this.bitArray = givenBitArray; this.DetermineLevel(); //// 2014/12 Time optimization //// this.CheckInstance(); } /// <summary> Initializes a new instance of the BinaryStructure class. </summary> /// <param name="givenSystem">Abstract system.</param> /// <param name="number">Number of structure.</param> public BinaryStructure(GeneralSystem givenSystem, long number) { this.gsystem = givenSystem ?? throw new InvalidOperationException("G-system is null."); this.DecimalNumber = number; this.SetNumber(number); this.DetermineLevel(); //// 2014/12 Time optimization //// this.CheckInstance(); } /// <summary> Initializes a new instance of the BinaryStructure class. </summary> /// <param name="structure">Binary structure.</param> public BinaryStructure(BinaryStructure structure) { Contract.Requires(structure != null); this.gsystem = structure.GSystem; this.bitArray = (BitArray)structure.BitArray.Clone(); this.level = structure.Level; //// 2014/12 Time optimization //// this.CheckInstance(); } #endregion #region Properties /// <summary> Gets BitArray. </summary> /// <value> Property description. </value> public BitArray BitArray { get { Contract.Ensures(Contract.Result<BitArray>() != null); if (this.bitArray == null) { throw new InvalidOperationException("Bit array is null."); } return this.bitArray; } } /// <summary> Gets or sets Number. </summary> /// <value> Property description. </value> public long Number { get; set; } /// <summary> Gets or sets DecimalNumber. </summary> /// <value> Property description. </value> public decimal DecimalNumber { get; set; } /// <summary> Gets or sets abstract G-System. </summary> /// <value> Property description. </value> [XmlIgnore] //// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Contracts", "Ensures")] public GeneralSystem GSystem { get { Contract.Ensures(Contract.Result<GeneralSystem>() != null); Contract.Ensures(Contract.Result<GeneralSystem>().Order > 0); if (this.gsystem == null) { throw new InvalidOperationException("G-system is null."); } return this.gsystem; } set => this.gsystem = value ?? throw new ArgumentException("Argument cannot be empty.", nameof(value)); } /// <summary> Gets or sets modality. </summary> /// <value> Property description. </value> [XmlIgnore] public IGeneralStruct Modality { get; set; } /// <summary> Gets or sets level, i.e. number of ones in the structure. </summary> /// <value> Property description. </value> [XmlAttribute] public byte Level { get => this.level; set { this.level = value; this.SetProperty(GenProperty.Level, this.level); } } /// <summary> /// Gets or sets the occurrence - for statistical reasons (material,...). May be moved to properties... /// </summary> /// <value> /// The occurrence. /// </value> public int Occurrence { get; set; } /// <summary> /// Gets or sets the class code. /// </summary> /// <value> /// The class code. /// </value> public string ClassCode { get; set; } #endregion #region Schemas /// <summary> Gets positions of nonzero bits. </summary> /// <value> Property description. </value> [XmlIgnore] public Collection<byte> BitPlaces { get { Contract.Ensures(Contract.Result<Collection<byte>>() != null); var places = new Collection<byte>(); var order = this.GSystem.Order; for (byte e = 0; e < order; e++) { if (this.IsOn(e)) { places.Add(e); } } return places; } } /// <summary> Gets distances of nonzero bits. </summary> /// <value> Property description. </value> [XmlIgnore] public Collection<byte> BitDistances { get { var places = this.BitPlaces; var distances = new Collection<byte>(); var order = this.GSystem.Order; if (this.Level > 1) { byte p = 0, r = 0; var lastL = (byte)(this.Level - 1); for (byte lev = 0; lev < lastL; lev++) { if (lev + 1 < places.Count) { p = places[lev]; //// p = this.PlaceAtLevel(lev); r = places[lev + 1]; //// r = this.PlaceAtLevel((byte)(lev + 1)); } distances.Add(this.GSystem.FormalLength(r - p)); //// + order } if (lastL < places.Count) { p = places[lastL]; //// p = this.PlaceAtLevel(lastL); } if (places.Count > 0) { r = places[0]; //// r = this.PlaceAtLevel(0); } distances.Add(this.GSystem.FormalLength(r - p)); //// + order } else { distances.Add(order); } return distances; } } /// <summary> /// Gets the number. /// </summary> /// <returns> Returns value. </returns> public long GetNumber { get { //// Contract.Requires(this.BitArray != null); if (this.bitArray == null) { return 0; } var r = this.bitArray.Count; long n = 0; for (byte bit = 0; bit < r; bit++) { if (this.bitArray[bit]) { n |= BinaryNumber.BitAt(bit); } } return n; } } /// <summary> /// Gets the class structure. /// </summary> /// <returns> Returns value. </returns> public BinaryStructure GetClassStructure { get { //// Contract.Requires(this.BitArray != null); if (this.bitArray == null) { return null; } var number = this.GetNumber; var classNumber = BinaryNumber.DetermineClassNumber(this.GSystem.Order, number); var chs = new BinaryStructure(this.GSystem, classNumber); return chs; } } /// <summary> /// Gets StructuralCode. /// </summary> /// <returns> Returns value. </returns> /// <value> Property description. </value> public string GetStructuralCode => this.structuralCode ?? (this.structuralCode = this.DetermineStructuralCode()); #endregion #region Static functions #endregion #region Static operators //// TICS rule 7@526: Reference types should not override the equality operator (==) //// public static bool operator ==(BinaryStructure structure1, BinaryStructure structure2) { return object.Equals(structure1, structure2); } //// public static bool operator !=(BinaryStructure structure1, BinaryStructure structure2) { return !object.Equals(structure1, structure2); } //// but TICS rule 7@530: Class implements interface 'IComparable' but does not implement '==' and '!='. /// <summary> /// Implements the operator <. /// </summary> /// <param name="object1">The object1.</param> /// <param name="object2">The object2.</param> /// <returns> /// Returns value. /// </returns> public static bool operator <(BinaryStructure object1, BinaryStructure object2) { if (object1 != null && object2 != null && (object1.Number > 0 || object2.Number > 0)) { return object1.Number < object2.Number; } return false; } /// <summary> /// Implements the operator >. /// </summary> /// <param name="object1">The object1.</param> /// <param name="object2">The object2.</param> /// <returns> /// Returns value. /// </returns> public static bool operator >(BinaryStructure object1, BinaryStructure object2) { if (object1 != null && object2 != null && (object1.Number > 0 || object2.Number > 0)) { return object1.Number > object2.Number; } return false; } /// <summary> /// Implements the operator <=. /// </summary> /// <param name="object1">The object1.</param> /// <param name="object2">The object2.</param> /// <returns> /// Returns value. /// </returns> public static bool operator <=(BinaryStructure object1, BinaryStructure object2) { if (object1 != null && object2 != null && (object1.Number > 0 || object2.Number > 0)) { return object1.Number <= object2.Number; } return false; } /// <summary> /// Implements the operator >=. /// </summary> /// <param name="object1">The object1.</param> /// <param name="object2">The object2.</param> /// <returns> /// Returns value. /// </returns> public static bool operator >=(BinaryStructure object1, BinaryStructure object2) { if (object1 != null && object2 != null && (object1.Number > 0 || object2.Number > 0)) { return object1.Number >= object2.Number; } return false; } #endregion #region Comparison /// <summary> Support sorting according to level and number. </summary> /// <param name="value">Object to be compared.</param> /// <returns> Returns value. </returns> public override int CompareTo(object value) { if (!(value is BinaryStructure bs)) { return 0; } if (this.Level < bs.Level) { return -1; } return this.Level > bs.Level ? 1 : string.Compare(this.BitArray.ToString(), bs.BitArray.ToString(), StringComparison.Ordinal); //// This kills the DataGrid //// throw new ArgumentException("Object is not a BinaryStructure"); } /// <summary> Test of equality. </summary> /// <param name="obj">Object to be compared.</param> /// <returns> Returns value. </returns> public override bool Equals(object obj) { //// check null (this pointer is never null in C# methods) if (object.ReferenceEquals(obj, null)) { return false; } if (object.ReferenceEquals(this, obj)) { return true; } if (this.GetType() != obj.GetType()) { return false; } return this.CompareTo(obj) == 0; } /// <summary> Support of comparison. </summary> /// <returns> Returns value. </returns> public override int GetHashCode() { return this.BitArray.GetHashCode(); } #endregion #region Public virtual methods /// <summary> Test of emptiness. </summary> /// <returns> Returns value. </returns> public virtual bool IsEmptyStruct() { return this.level == 0; } /// <summary> Validity test. </summary> /// <returns> Returns value. </returns> public virtual bool IsValidStruct() { return true; } /// <summary> Evaluate properties of the structure. Used in descendant objects. /// Must be virtual, because of call from StructuralVariety. </summary> public virtual void DetermineBehavior() { } #endregion #region Public methods /// <summary> Makes a deep copy of the BinaryStructure object. </summary> /// <returns> Returns object. </returns> [JetBrains.Annotations.PureAttribute] public override object Clone() { return new BinaryStructure(this.GSystem, this.GetStructuralCode); } /// <summary> /// Determine and sets the level property. /// </summary> /// <param name="givenBit">Given bit of the structure.</param> /// <returns> Returns level containing the given bit. </returns> public byte LevelOfBit(byte givenBit) { if (givenBit == 0) { return 0; } var bitLevel = -1; //// byte order = this.GSystem.Order; for (byte e = 0; e <= givenBit; e++) { if (this.IsOn(e)) { bitLevel++; } } var lev = (byte)((bitLevel >= 0) ? bitLevel : 0); return lev; } /// <summary> Determine and sets the level property. </summary> public void DetermineLevel() { this.level = this.IsOnInRange(0, (byte)(this.GSystem.Order - 1)); this.Properties[GenProperty.Level] = this.level; // used with Qualifiers } #endregion #region Bit gettings /// <summary> Gets a value indicating whether is the bit ON. </summary> /// <param name="element">Element of system.</param> /// <returns> Returns value. </returns> public bool IsOn(byte element) { return element < this.BitArray.Count && this.BitArray[element]; } /// <summary> Gets a value indicating whether is the bit OFF. </summary> /// <param name="element">Requested element.</param> /// <returns> Returns value.</returns> public bool IsOff(byte element) { if (element >= this.BitArray.Count) { return true; } return !this.BitArray[element]; } /// <summary> Returns number of bits in selected Range, that are ON. </summary> /// <param name="elementFrom">First element of system.</param> /// <param name="elementTo">Second element of system.</param> /// <returns> Returns value.</returns> public byte IsOnInRange(byte elementFrom, byte elementTo) { byte s = 0; for (var e = elementFrom; e <= elementTo; e++) { if (this.IsOn(e)) { s++; } } return s; } /// <summary> Returns number of bits in selected Range, that are OFF. </summary> /// <param name="elementFrom">Fist element of system.</param> /// <param name="elementTo">Second element of system.</param> /// <returns>Returns value.</returns> public byte IsOffInRange(byte elementFrom, byte elementTo) { byte s = 0; for (var e = elementFrom; e <= elementTo; e++) { if (this.IsOff(e)) { s++; } } return s; } #endregion #region Bit setting /// <summary> Sets all bits ON. </summary> public void OnAll() { this.BitArray.SetAll(true); } /// <summary> Sets selected bit ON. </summary> /// <param name="element">Requested element.</param> public void On(byte element) { if (element >= this.BitArray.Length) { return; } this.BitArray[element] = true; } /// <summary> /// Sets bits in selected Range ON. </summary> /// <param name="elementFrom">Fist element of system.</param> /// <param name="elementTo">Second element of system.</param> public void OnRange(byte elementFrom, byte elementTo) { for (var e = elementFrom; e <= elementTo; e++) { this.On(e); } } /// <summary> Sets all bits OFF. </summary> public void OffAll() { this.BitArray.SetAll(false); } /// <summary> Sets selected bit OFF. </summary> /// <param name="element">Requested element.</param> public void Off(byte element) { if (element >= this.BitArray.Length) { return; } this.BitArray[element] = false; } /// <summary> Sets bits in selected Range OFF. </summary> /// <param name="elementFrom">Fist element of system.</param> /// <param name="elementTo">Second element of system.</param> public void OffRange(byte elementFrom, byte elementTo) { for (var e = elementFrom; e <= elementTo; e++) { this.Off(e); } } #endregion #region String representation /// <summary> Binary schema of the structure. </summary> /// <returns> Returns value. </returns> public virtual string ElementString() { var s = new StringBuilder(); //// string test = this.BitArray.ToString(); for (byte e = 0; e < this.GSystem.Order; e++) { s.Append(this.IsOn(e) ? '1' : '0'); } return s.ToString(); } /// <summary> Inverse binary schema of the structure. </summary> /// <returns> Returns value. </returns> public string InverseElementString() { var s = new StringBuilder(); for (int e = (byte)(this.GSystem.Order - 1); e >= 0; e--) { s.Append(this.IsOn((byte)e) ? '1' : '0'); } return s.ToString(); } /// <summary> String representation of the object. </summary> /// <returns> Returns value. </returns> public override string ToString() { if (this.bitArray == null) { return string.Empty; } var s = new StringBuilder(); s.AppendFormat(CultureInfo.CurrentCulture, "<{0}", this.BitArray); s.AppendFormat(CultureInfo.CurrentCulture, "L{0,2}", this.level.ToString("D", CultureInfo.CurrentCulture.NumberFormat)); s.Append(" "); s.Append(this.ElementString()); //// s.Append(givenClassNumber); s.Append:this.tran; s.Append:this.rota; return s.ToString(); } #endregion #region Structural code /// <summary> Determine and sets the level property. </summary> /// <param name="givenStructuralCode">Structural code.</param> public void SetStructuralCode(string givenStructuralCode) { this.structuralCode = givenStructuralCode; this.bitArray = new BitArray(this.GSystem.Order); if (string.IsNullOrWhiteSpace(this.structuralCode)) { return; } var codes = this.structuralCode.Split(','); Array.ForEach( codes, code => { int element = byte.Parse(code, CultureInfo.CurrentCulture); if (element < this.BitArray.Length) { this.BitArray[element] = true; } }); } /// <summary> Determine and sets the level property. </summary> /// <returns>Returns value.</returns> public string DetermineStructuralCode() { var code = string.Empty; if (this.level == 0) { return code; } var places = this.BitPlaces; var first = true; foreach (var place in places) { if (first) { first = false; } else { code += ","; } code += place.ToString(CultureInfo.CurrentCulture); } return code; } #endregion #region Private methods /// <summary> /// Sets the number. /// </summary> /// <param name="number">The number.</param> private void SetNumber(long number) { this.Number = number; this.bitArray = new BitArray(this.gsystem.Order); var bn = new BinaryNumber(this.gsystem, number); for (byte i = 0; i < this.gsystem.Order && i < this.BitArray.Count; i++) { this.BitArray[i] = bn.IsOn(i); } } #endregion } }